package middleware

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gitee.com/danlansky/go-library/logs"
	"github.com/gin-gonic/gin"
	"github.com/willf/pad"
	"go.uber.org/zap"
	"io/ioutil"
	"time"
)

type bodyLogWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (w bodyLogWriter) Write(b []byte) (int, error) {
	w.body.Write(b)
	return w.ResponseWriter.Write(b)
}

// Logging is a middleware function that logs the each request.
func Logging() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now().UTC()

		// Read the Body content
		var bodyBytes []byte
		if c.Request.Body != nil {
			bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
		}

		// Restore the io.ReadCloser to its original state
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

		// The basic informations.
		method := c.Request.Method
		ip := c.ClientIP()

		//log.Debugf("New request come in, path: %s, Method: %s, body `%s`", path, method, string(bodyBytes))
		blw := &bodyLogWriter{
			body:           bytes.NewBufferString(""),
			ResponseWriter: c.Writer,
		}
		c.Writer = blw

		// Continue.
		c.Next()

		// Calculates the latency.
		end := time.Now().UTC()
		latency := end.Sub(start)

		var code int
		var message string

		// 移除json不必要的空格，即json字符串压缩
		var postJsonStr bytes.Buffer
		_ = json.Compact(&postJsonStr, bodyBytes)

		// get code and message
		if json.Valid(blw.body.Bytes()) == false {
			code = 500
			message = "err response,not json"
		} else {
			code = 200
			message = "ok"
		}

		str := fmt.Sprintf("%s|%s|%s|%d|%s|{code: %d, message: %s}", latency, ip, pad.Right(method, 5, ""), blw.ResponseWriter.Status(), c.Request.URL.RequestURI(), code, message)
		logs.AccessLog(str, zap.String("reqid", c.GetString("X-Request-Id")), zap.String("post", postJsonStr.String()))
	}
}
